| Conditions | 43 |
| Paths | 13464 |
| Total Lines | 207 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 3 | ||
| Bugs | 1 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like wallet_sweeper.js ➔ WalletSweeper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | var UnspentOutputFinder = require('./unspent_output_finder'); |
||
| 20 | var WalletSweeper = function(backupData, bitcoinDataClient, options) { |
||
| 21 | /* jshint -W071, -W074 */ |
||
| 22 | var self = this; |
||
| 23 | this.defaultSettings = { |
||
| 24 | network: 'btc', |
||
| 25 | testnet: false, |
||
| 26 | regtest: false, |
||
| 27 | logging: false, |
||
| 28 | bitcoinCash: false, |
||
| 29 | sweepBatchSize: 200 |
||
| 30 | }; |
||
| 31 | this.settings = _.merge({}, this.defaultSettings, options); |
||
| 32 | this.bitcoinDataClient = bitcoinDataClient; |
||
| 33 | this.utxoFinder = new UnspentOutputFinder(bitcoinDataClient, this.settings); |
||
| 34 | this.sweepData = null; |
||
| 35 | |||
| 36 | // set the bitcoinlib network |
||
| 37 | if (typeof options.network === "object") { |
||
| 38 | this.network = options.network; |
||
| 39 | } else { |
||
| 40 | this.network = this.getBitcoinNetwork(this.settings.network, this.settings.testnet, this.settings.regtest); |
||
| 41 | } |
||
| 42 | |||
| 43 | backupData.walletVersion = backupData.walletVersion || 2; //default to version 2 wallets |
||
| 44 | |||
| 45 | var usePassword = false; |
||
| 46 | |||
| 47 | // validate backup data, cleanup input, and prepare seeds |
||
| 48 | if (!Array.isArray(backupData.blocktrailKeys)) { |
||
| 49 | throw new Error('blocktrail pub keys are required (must be type Array)'); |
||
| 50 | } |
||
| 51 | |||
| 52 | switch (backupData.walletVersion) { |
||
| 53 | case 1: |
||
| 54 | if (typeof backupData.primaryMnemonic === "undefined" || !backupData.primaryMnemonic) { |
||
| 55 | throw new Error('missing primary mnemonic for version 1 wallet'); |
||
| 56 | } |
||
| 57 | if (typeof backupData.backupMnemonic === "undefined" || !backupData.backupMnemonic) { |
||
| 58 | throw new Error('missing backup mnemonic for version 1 wallet'); |
||
| 59 | } |
||
| 60 | if (typeof backupData.primaryPassphrase === "undefined") { |
||
| 61 | throw new Error('missing primary passphrase for version 1 wallet'); |
||
| 62 | } |
||
| 63 | |||
| 64 | // cleanup copy paste errors from mnemonics |
||
| 65 | backupData.primaryMnemonic = backupData.primaryMnemonic.trim() |
||
| 66 | .replace(new RegExp("\r\n", 'g'), " ") |
||
| 67 | .replace(new RegExp("\n", 'g'), " ") |
||
| 68 | .replace(/\s+/g, " "); |
||
| 69 | backupData.backupMnemonic = backupData.backupMnemonic.trim() |
||
| 70 | .replace(new RegExp("\r\n", 'g'), " ") |
||
| 71 | .replace(new RegExp("\n", 'g'), " ") |
||
| 72 | .replace(/\s+/g, " "); |
||
| 73 | break; |
||
| 74 | |||
| 75 | case 2: |
||
| 76 | case 3: |
||
| 77 | if (typeof backupData.encryptedPrimaryMnemonic === "undefined" || !backupData.encryptedPrimaryMnemonic) { |
||
| 78 | throw new Error('missing encrypted primary seed for version 2 wallet'); |
||
| 79 | } |
||
| 80 | if (typeof backupData.backupMnemonic === "undefined" || (!backupData.backupMnemonic && backupData.backupMnemonic !== false)) { |
||
| 81 | throw new Error('missing backup seed for version 2 wallet'); |
||
| 82 | } |
||
| 83 | //can either recover with password and password encrypted secret, or with encrypted recovery secret and a decryption key |
||
| 84 | usePassword = typeof backupData.password !== "undefined" && backupData.password !== null; |
||
| 85 | if (usePassword) { |
||
| 86 | if (typeof backupData.passwordEncryptedSecretMnemonic === "undefined" || !backupData.passwordEncryptedSecretMnemonic) { |
||
| 87 | throw new Error('missing password encrypted secret for version 2 wallet'); |
||
| 88 | } |
||
| 89 | if (typeof backupData.password === "undefined") { |
||
| 90 | throw new Error('missing primary passphrase for version 2 wallet'); |
||
| 91 | } |
||
| 92 | } else { |
||
| 93 | if (typeof backupData.encryptedRecoverySecretMnemonic === "undefined" || !backupData.encryptedRecoverySecretMnemonic) { |
||
| 94 | throw new Error('missing encrypted recovery secret for version 2 wallet (recovery without password)'); |
||
| 95 | } |
||
| 96 | if (!backupData.recoverySecretDecryptionKey) { |
||
| 97 | throw new Error('missing recovery secret decryption key for version 2 wallet (recovery without password)'); |
||
| 98 | } |
||
| 99 | } |
||
| 100 | |||
| 101 | // cleanup copy paste errors from mnemonics |
||
| 102 | backupData.encryptedPrimaryMnemonic = backupData.encryptedPrimaryMnemonic.trim() |
||
| 103 | .replace(new RegExp("\r\n", 'g'), " ") |
||
| 104 | .replace(new RegExp("\n", 'g'), " ") |
||
| 105 | .replace(/\s+/g, " "); |
||
| 106 | backupData.backupMnemonic = (backupData.backupMnemonic || "").trim() |
||
| 107 | .replace(new RegExp("\r\n", 'g'), " ") |
||
| 108 | .replace(new RegExp("\n", 'g'), " ") |
||
| 109 | .replace(/\s+/g, " "); |
||
| 110 | if (backupData.recoverySecretDecryptionKey) { |
||
| 111 | backupData.recoverySecretDecryptionKey = backupData.recoverySecretDecryptionKey.trim() |
||
| 112 | .replace(new RegExp("\r\n", 'g'), " ") |
||
| 113 | .replace(new RegExp("\n", 'g'), " ") |
||
| 114 | .replace(/\s+/g, " "); |
||
| 115 | } |
||
| 116 | if (usePassword) { |
||
| 117 | backupData.passwordEncryptedSecretMnemonic = backupData.passwordEncryptedSecretMnemonic.trim() |
||
| 118 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
| 119 | } else { |
||
| 120 | backupData.encryptedRecoverySecretMnemonic = backupData.encryptedRecoverySecretMnemonic.trim() |
||
| 121 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
| 122 | } |
||
| 123 | |||
| 124 | break; |
||
| 125 | |||
| 126 | default: |
||
| 127 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
| 128 | } |
||
| 129 | |||
| 130 | |||
| 131 | // create BIP32 HDNodes for the Blocktrail public keys |
||
| 132 | this.blocktrailPublicKeys = {}; |
||
| 133 | _.each(backupData.blocktrailKeys, function(blocktrailKey) { |
||
| 134 | self.blocktrailPublicKeys[blocktrailKey['keyIndex']] = bitcoin.HDNode.fromBase58(blocktrailKey['pubkey'], self.network); |
||
| 135 | }); |
||
| 136 | |||
| 137 | // convert the primary and backup mnemonics to seeds (using BIP39) |
||
| 138 | var primarySeed, backupSeed, secret; |
||
| 139 | switch (backupData.walletVersion) { |
||
| 140 | case 1: |
||
| 141 | primarySeed = bip39.mnemonicToSeed(backupData.primaryMnemonic, backupData.primaryPassphrase); |
||
| 142 | backupSeed = bip39.mnemonicToSeed(backupData.backupMnemonic, ""); |
||
| 143 | break; |
||
| 144 | |||
| 145 | case 2: |
||
| 146 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
| 147 | backupData.encryptedPrimaryMnemonic = blocktrail.convert(bip39.mnemonicToEntropy(backupData.encryptedPrimaryMnemonic), 'hex', 'base64'); |
||
| 148 | if (usePassword) { |
||
| 149 | backupData.passwordEncryptedSecretMnemonic = blocktrail.convert( |
||
| 150 | bip39.mnemonicToEntropy(backupData.passwordEncryptedSecretMnemonic), 'hex', 'base64'); |
||
| 151 | } else { |
||
| 152 | backupData.encryptedRecoverySecretMnemonic = blocktrail.convert( |
||
| 153 | bip39.mnemonicToEntropy(backupData.encryptedRecoverySecretMnemonic), 'hex', 'base64'); |
||
| 154 | } |
||
| 155 | |||
| 156 | // decrypt encryption secret |
||
| 157 | if (usePassword) { |
||
| 158 | secret = CryptoJS.AES.decrypt(backupData.passwordEncryptedSecretMnemonic, backupData.password).toString(CryptoJS.enc.Utf8); |
||
| 159 | } else { |
||
| 160 | secret = CryptoJS.AES.decrypt(backupData.encryptedRecoverySecretMnemonic, backupData.recoverySecretDecryptionKey).toString(CryptoJS.enc.Utf8); |
||
| 161 | } |
||
| 162 | |||
| 163 | if (!secret) { |
||
| 164 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
| 165 | } |
||
| 166 | |||
| 167 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
| 168 | primarySeed = new Buffer(CryptoJS.AES.decrypt(backupData.encryptedPrimaryMnemonic, secret).toString(CryptoJS.enc.Utf8), 'base64'); |
||
|
|
|||
| 169 | |||
| 170 | if (backupData.backupMnemonic) { |
||
| 171 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
| 172 | } |
||
| 173 | |||
| 174 | break; |
||
| 175 | |||
| 176 | case 3: |
||
| 177 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
| 178 | backupData.encryptedPrimaryMnemonic = EncryptionMnemonic.decode(backupData.encryptedPrimaryMnemonic); |
||
| 179 | if (usePassword) { |
||
| 180 | backupData.passwordEncryptedSecretMnemonic = EncryptionMnemonic.decode(backupData.passwordEncryptedSecretMnemonic); |
||
| 181 | } else { |
||
| 182 | backupData.encryptedRecoverySecretMnemonic = EncryptionMnemonic.decode(backupData.encryptedRecoverySecretMnemonic); |
||
| 183 | } |
||
| 184 | |||
| 185 | // decrypt encryption secret |
||
| 186 | if (usePassword) { |
||
| 187 | secret = Encryption.decrypt(backupData.passwordEncryptedSecretMnemonic, new Buffer(backupData.password)); |
||
| 188 | } else { |
||
| 189 | secret = Encryption.decrypt(backupData.encryptedRecoverySecretMnemonic, new Buffer(backupData.recoverySecretDecryptionKey, 'hex')); |
||
| 190 | } |
||
| 191 | |||
| 192 | if (!secret) { |
||
| 193 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
| 194 | } |
||
| 195 | |||
| 196 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
| 197 | primarySeed = Encryption.decrypt(backupData.encryptedPrimaryMnemonic, secret); |
||
| 198 | if (backupData.backupMnemonic) { |
||
| 199 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
| 200 | } |
||
| 201 | |||
| 202 | break; |
||
| 203 | |||
| 204 | default: |
||
| 205 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
| 206 | } |
||
| 207 | |||
| 208 | // convert the primary and backup seeds to private keys (using BIP32) |
||
| 209 | this.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(primarySeed, this.network); |
||
| 210 | |||
| 211 | if (backupSeed) { |
||
| 212 | this.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(backupSeed, this.network); |
||
| 213 | this.backupPublicKey = this.backupPrivateKey.neutered(); |
||
| 214 | } else { |
||
| 215 | this.backupPrivateKey = false; |
||
| 216 | this.backupPublicKey = bitcoin.HDNode.fromBase58(backupData.backupPublicKey, this.network); |
||
| 217 | } |
||
| 218 | |||
| 219 | if (this.settings.logging) { |
||
| 220 | console.log('using password method: ' + usePassword); |
||
| 221 | console.log("Primary Prv Key: " + this.primaryPrivateKey.toBase58()); |
||
| 222 | console.log("Primary Pub Key: " + this.primaryPrivateKey.neutered().toBase58()); |
||
| 223 | console.log("Backup Prv Key: " + (this.backupPrivateKey ? this.backupPrivateKey.toBase58() : null)); |
||
| 224 | console.log("Backup Pub Key: " + this.backupPublicKey.toBase58()); |
||
| 225 | } |
||
| 226 | }; |
||
| 227 | |||
| 741 |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.